module Core where

import IO
import Char

data Cell val = Empty |
                Text String |
                Number Double |
                Sum [(Int, Int)] |
                Prod [(Int, Int)] |
                Avg [(Int, Int)]
                deriving (Show, Read, Eq)


containsTuple (_, _) [] = False
containsTuple (x, y) (l:ls) = if (x, y) == l then True
                              else containsTuple (x, y) ls

isEmpty [] = True
isEmpty (x:_) = False

startsWith [] _ = False
startsWith _ [] = True 
startsWith (a:as) (b:bs) = if a == b then startsWith as bs
                           else False
getLength [] = 0
getLength (x:xs) = 1 + getLength xs 

getRows xs = getLength xs

getCols (x:xs) = if getLength (x:xs) > 0 then getLength x
                 else 0

initList1D 0 _ = []
initList1D n a = a : (initList1D (n - 1) a)

initList2D 0 _ _ = []
initList2D n m val = (initList1D m val) : (initList2D (n - 1) m val)

initSpreadSheet n m = initList2D n m Empty

insertValue1D (x:xs) 0 val = val : xs
insertValue1D (x:xs) n val = x : insertValue1D xs (n - 1) val

insertValue2D (x:xs) 0 m val = (insertValue1D x m val) : xs
insertValue2D (x:xs) n m val =  x : (insertValue2D xs (n - 1) m val)

insertValue xs n m val = insertValue2D xs n m val

addRow xs 0 cols = (initList1D cols Empty) : xs
addRow [] _ _ = []
addRow (x:xs) row cols = x : (addRow xs (row - 1) cols) 

deleteRow (_:xs) 0 = xs
deleteRow (x:xs) row = x : (deleteRow xs (row - 1))

insertColumnIntoRow xs 0 val = val : xs
insertColumnIntoRow [] _ _ = [] 
insertColumnIntoRow (x:xs) col val = x : insertColumnIntoRow xs (col - 1) val

insertColumn [] _ = []
insertColumn (x:xs) col = insertColumnIntoRow x col Empty : insertColumn xs col

deleteColumnFromRow (_:xs) 0 = xs
deleteColumnFromRow [] _ = []
deleteColumnFromRow (x:xs) col = x : deleteColumnFromRow xs (col - 1)  

deleteColumn [] _ = []
deleteColumn (x:xs) col = (deleteColumnFromRow x col) : (deleteColumn xs col)

getCellValue (Number val) = val
getCellValue _ = 0

forEachCell _ a [] _ _ = a
forEachCell f a (c:cs) row col = f (getCellValue c) (forEachCell f a cs row (col + 1))

forEachRow _ a [] _ = a 
forEachRow f a (r:rs) row = f (forEachCell f a r row 0) (forEachRow f a rs (row + 1))
   
forEach f a spreadsheet = forEachRow f a spreadsheet 0

forEachCellConditional _ a [] _ _ _ = a
forEachCellConditional f a (c:cs) row col cells = if containsTuple (row, col) cells then
                                                    f (getCellValue c) (forEachCellConditional f a cs row (col + 1) cells)
                                                  else
                                                    f (a) (forEachCellConditional f a cs row (col + 1) cells)

forEachRowConditional _ a [] _ _ = a 
forEachRowConditional f a (r:rs) row cells = f (forEachCellConditional f a r row 0 cells) (forEachRowConditional f a rs (row + 1) cells)
   
forEachConditional f a spreadsheet cells = forEachRowConditional f a spreadsheet 0 cells

sumAll spreadsheet cells = forEachConditional (+) 0 spreadsheet cells
multiplyAll spreadsheet cells = forEachConditional (*) 1 spreadsheet cells
avgAll spreadsheet cells = sum / count
                           where sum = (sumAll spreadsheet cells)
                                 count = (getLength cells) 

isNumber [] = False
isNumber [x] = if isDigit x then True else False
isNumber (x:xs) = if isDigit x || x == '-' then isNumber xs else False

isReal [] = False
isReal [x] = if isDigit x then True else False
isReal (x:xs) = if isDigit x || x == '-' || x == '.' then isReal xs else False

isText [] = False
isText [x] = if isAlpha x then True else False
isText (x:xs) = if isAlpha x then isText xs else False
                     
split [] delim = [""]
split (c:cs) delim | c == delim = "" : rest
                   | otherwise = (c : head rest) : tail rest
                      where
                        rest = split cs delim
                                                            
writeRow _ [] = do
                   return (0)

writeRow file [c] = do
                       hPutStr file (show c)
                       return (0)

writeRow file (c:cs) = do
                          hPutStr file ((show c) ++ "\t")
                          writeRow file cs

writeRows _ [] = do
                    return (0)

writeRows file (r:rs) = do
                           writeRow file r
                           hPutStrLn file ""
                           writeRows file rs

writeSpreadsheetToFile fileName spreadsheet = do
                                                 file <- openFile fileName WriteMode
                                                 writeRows file spreadsheet
                                                 hClose file

parseCell cs = (read cs) :: Cell [(Integer, Integer)]

parseCells [] = []
parseCells (x:xs) = parseCell x : (parseCells xs) 

readRow file = do
                  line <- hGetLine file
                  let values = split line '\t'                  
                  return (parseCells values)

readRows file = do
                   isEof <- hIsEOF file
                   if isEof == False then
                     do
                       row <- readRow file
                       rest <- readRows file 
                       return (row : rest)
                   else
                     return ([])
                                                 
readSpreadsheetFromFile fileName = do 
                                      file <- openFile fileName ReadMode
                                      result <- readRows file
                                      hClose file
                                      return (result)

cellRangeToList [(a, b), (c, d)] = [ (x, y) | x <- [a .. c], y <- [b .. d]]